/** @file stream.h
	@brief Stream interface */

#ifndef G3_STREAM_H
#define G3_STREAM_H

#include <string.h>
#include <stdlib.h>
#include <string>

#include "types.h"
#include "log.h"

/** The length of printf buffer */
#define STREAM_PRINT_BUFFER     5120

namespace G3 {

/** @class STREAM
	@brief Stream interface */
class STREAM {
	public:
        /**
            Empty constructor
        */
		STREAM ();
        /**
            Overloadable destructor
        */
		virtual ~STREAM ();

        /** Seeking flags */
        enum STREAM_SEEK_FLAG {
            /** Seek to the beginning */
            STREAM_BEGINNING = 0,
            /** Seek to a certain byte in the file */
            STREAM_SET = 1,
            /** Tell current seek position */
            STREAM_GET = 1,
            /** Seek forward by a given amount of bytes (can be negative) */
            STREAM_MOVE = 2,
            /** Seek to the end of the file */
            STREAM_END = 3,
            /** Tell the size of the file */
            STREAM_SIZE = 4,

            /** For debugging purposes only */
            STREAM_REL_BEGIN = 5,
            /** For debugging purposes only */
            STREAM_REL_GET = 6,
            /** For debugging purposes only */
            STREAM_REL_END = 7
        };

        /**
            @struct CHUNK
            @brief A chunk of data
        */
        struct CHUNK {
            /**
                @brief Constructor
            */
            CHUNK ();
            /**
                @brief Destructor
            */
            ~CHUNK ();

            /** Size of the chunk (in bytes) */
            long chunkSize;
            /** Data pointer */
            byte *chunkPointer;

            /**
                @brief Assignment operator for data copying
                @param[in] chunk Reference to the chunk to be copied
                @return Reference to the freshly assigned chunk
            */
            CHUNK& operator= (const CHUNK &chunk);
        };

        /** Pointer to the log. Points to G3::MainLog (file "engine.log") by default. */
		G3::LOG *log;

        /** Contains the last line separator / delimiter that was found */
		char lastLineDelimiter;

        /** Name of the current file */
		std::string fileName;

        /** Stream-specific flags */
		ushort flags;

        /**
            @brief Enables a flag
        */
		void Disable (ushort flag);
        /**
            @brief Disables a flag
        */
		void Enable (ushort flag);

        /**
            @brief Gets the next chunk of data
            @param[in] maxSize The maximal size of the chunk
            @return The data chunk
        */
		virtual CHUNK GetChunk (long maxSize = -1) {
		    CHUNK c;

		    Assert (false, "Pure virtual function STREAM::GetChunk called..\n");

		    return c;
        }

        /**
            @brief Gets a pointer to the buffer data
            @return The pointer
        */
		virtual byte *GetDataPointer () {
		    Assert (false, "Pure virtual function STREAM::GetDataPointer called..\n");

		    return NULL;
        }

//==============================================================================
// Sets stream caret position
//
//		long Offset				File position to seek for
//
//		STREAM_SEEK_FLAG Flag
//			STREAM_BEGINNING	File position caret is reset to the beginning of
//								current stream buffer
//
//			STREAM_SET			Sets file position to Offset
//
//          STREAM_MOVE         Moves file position by Offset
//
//			STREAM_END			File position caret is moved to the end of the
//								stream
//
//	Returns true on success
//==============================================================================
        /**
            @brief Sets stream caret position
            @param[in] offset File position to seek for
            @param[in] flag What to seek for ? \see STREAM_SEEK_FLAG.
            @return True on success, false on failure
        */
		virtual bool Seek (long offset, STREAM_SEEK_FLAG flag) {
		    Assert (false, "Pure virtual function STREAM::Seek called..\n");

		    return false;
        }

//==============================================================================
// Tells the stream caret position
//
//		STREAM_SEEK_FLAG Flag
//			STREAM_GET			Tells the CaretPosition of the stream
//
//			STREAM_END			Tells the size of the stream
//
//          STREAM_SIZE         Tells the amount of memory reserved for the stream
//
//	Returns the position in bytes or -1 if the input was invalid
//==============================================================================
        /**
            @brief Tells the stream caret position
            @param[in] flag What to ask for ? \see STREAM_SEEK_FLAG.
            @return The value that was asked
        */
		virtual long Tell (STREAM_SEEK_FLAG flag) {
		    Assert (false, "Pure virtual function STREAM::Tell called..\n");

		    return 0;
        }

        /**
            @brief EOF check
            @return True if End Of File was encountered
        */
		virtual bool EndOfFile () {
		    Assert (false, "Pure virtual function STREAM::EndOfFile called..\n");

		    return false;
        }

        /**
            @brief Resizes the stream buffer
            @param[in] size The new size of the buffer
        */
		virtual void Resize (long size) {
		    Assert (false, "Pure virtual function STREAM::Resize called..\n");
        }

        /**
            @brief Reads data from the stream
            @param[out] pointer Pointer to a memory block where the data is stored
            @param[in] size Number of bytes read from the stream
            @param[in] flag Some kind of flag
            @return The number of bytes read
        */
		virtual long Read (byte *pointer, long size, bool flag = false) {
		    Assert (false, "Pure virtual function STREAM::Read called..\n");

		    return 0;
        }

        /**
            @brief Reads a line from the stream. End of a line can be overridden..
            @param[in] delimiters End of line characters ("\r\n" is default)
            @return Everything that it was able to read from the stream until "\r", "\n" or EOF
        */
		virtual std::string Readline (std::string delimiters) {
		    Assert (false, "Pure virtual function STREAM::Readline called..\n");

		    return "";
        }

        /**
            @brief Reads data from the stream and saves it into a file
            @param[in] filename Name of the file to store the data in
            @param[in] size Number of bytes read from the stream (-1 or 0 reads the whole stream)
            @param[in] position Where to write into the file
            @return The number of bytes read
        */
		virtual long ReadToFile (const string &filename, long size = -1, long position = 0) {
		    Assert (false, "Pure virtual function STREAM::ReadToFile called..\n");

		    return 0;
        }

        /**
            @brief Flushes the stream (erases data that has already been read)
            @return True on success, false on failure
        */
		virtual bool Flush () {
		    Assert (false, "Pure virtual function STREAM::Flush called..\n");

		    return false;
        }

        /**
            @brief Adds to the stream buffer
            @param[in] pointer Pointer to a memory block from where to buffer
            @param[in] size Number of bytes to buffer
            @return Size on success, something else on failure
        */
		virtual long Buffer (byte *pointer, long size) {
		    Assert (false, "Pure virtual function STREAM::Buffer called..\n");

		    return 0;
        }

        /**
            @brief Buffers a file to the stream
            @param[in] filename Name of the file to buffer
            @param[in] size Number of bytes to buffer
            @param[in] position Where in the file should we start reading
            @return Size on success, something else on failure
        */
		virtual long BufferFromFile (const string &filename, long size, long position) {
		    Assert (false, "Pure virtual function STREAM::BufferFromFile called..\n");

		    return 0;
        }

        /**
            @brief Prints into the buffer
            @param[in] fmt Printf - like format string
            @return Number of buffered bytes
        */
		virtual long BufferPrint (const char *fmt, ...) {
		    Assert (false, "Pure virtual function STREAM::BufferPrint called..\n");

		    return 0;
        }

        /**
            @brief Dumps the stream into the log
        */
		virtual void DumpToLog () {
		    Assert (false, "Pure virtual function STREAM::DumpToLog called..\n");
        }

        /** Is caret locked ? */
        bool caretLocked;
        /** Is buffer locked for reading ? */
        bool bufferReadLocked;
        /** Is buffer locked for writing ? */
        bool bufferWriteLocked;
};


/**
    @brief A simple file size query function
    @param[in] filename Path to the file
    @return The size of the file
    \todo Maybe this should be in the filehandler header ?
*/
long GetFileSize (const string &filename);

}

#endif
